package com.bytefy.server;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mozilla.javascript.ClassShutter;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bytefy.core.Timer;
import com.bytefy.server.js.SandboxContextFactory;
import com.bytefy.server.sandbox.Response;
import com.bytefy.server.sandbox.UserDB;

@SuppressWarnings("serial")
public class JavascriptServlet extends HttpServlet {
	private Logger log = LoggerFactory.getLogger(getClass());
	
	protected static final String[] GRANTED_PACKAGES = { "org.mortbay.jetty",
			"com.bytefy.server.sandbox", "java.lang" };

	private String readFile(String path) throws IOException {
		log.info("read file "+path);
		path = "/".equals(path) ? "/index.html" : path;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		byte[] buf = new byte[1024];
		int read = 0;
		if (path.endsWith(".html")) {
			File scriptPath = new File("js/" + path );
			FileInputStream in = new FileInputStream(scriptPath);
			while ((read = in.read(buf)) != -1) {
				out.write(buf, 0, read);
			}
			String source = new String(out.toByteArray());
			Pattern p =  Pattern.compile("<%=(.+)%", Pattern.DOTALL);
			Matcher m = p.matcher(source);
			while(m.find()) {
				System.out.println(m.group());
			}
			return new String(out.toByteArray());
		} else {
			File scriptPath = new File("js" + path+".js");
			System.out.println(scriptPath);
			if (!scriptPath.exists()) return readFile("index.html");
			FileInputStream in = new FileInputStream(scriptPath);
			while ((read = in.read(buf)) != -1) {
				out.write(buf, 0, read);
			}
			return new String(out.toByteArray());
		}
	}

	public JavascriptServlet() {
		ContextFactory.initGlobal(new SandboxContextFactory());
	}

	private Set<String> allowedClasses = new HashSet<String>();

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html; charset=utf-8");
		Timer t = new Timer();
		t.start();
		Context cx = Context.enter();
		cx.setClassShutter(new ClassShutter() {
			@Override
			public boolean visibleToScripts(String arg0) {
				if (allowedClasses.contains(arg0))
					return true;
				for (String p : GRANTED_PACKAGES)
					if (arg0.startsWith(p))
						return true;
				return false;
			}
		});
		try {
			String source = readFile(request.getPathInfo());
			if (source == null) throw new Exception("Can't read source");
			Scriptable scope = cx.initStandardObjects();
			addObject("db", new UserDB("guest"), scope);
			if (request.getPathInfo().startsWith("/services/")) {

			} else {
				addObject("request", request, scope);
				addObject("response", new Response(response), scope);
			}
			cx.evaluateString(scope, source, source, 1, null);
		} catch (FileNotFoundException e) {
			try {
				response.sendError(404, "Page not found");
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} catch (Throwable e) {
			try {
				response.getWriter().println("Error: "+e.getMessage());
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} finally {
			Context.exit();
		}
		t.stop();
		double time = t.getTime();
	}

	private void addObject(String name, Object o, Scriptable scope) {
		Object wrapper = Context.javaToJS(o, scope);
		ScriptableObject.putProperty(scope, name, wrapper);
	}

	private Integer threads = 0;
}
